/*****************************************************************************
*    Ali Corp. All Rights Reserved. 2002 Copyright (C)
*
*    File:    bootloader.c
*
*    Description:    This file contains all globe micros and functions declare
*                    of Flash boot loader.
*    History:
*           Date            Athor        Version          Reason
*       ============    =============   =========   =================
*   1.  Oct.28.2003     Justin Wu       Ver 0.1    Create file.
 *  2.  2006.5.16       Justin Wu       Ver 0.2    STO chunk support & clean up.
*****************************************************************************/
#include <sys_config.h>
#include <sys_parameters.h>
#include <api/libc/printf.h>
#include <api/libc/string.h>
#include <api/libchunk/chunk.h>
#include <bus/sci/sci.h>
#include <bus/flash/flash.h>
#include <hal/hal_gpio.h>
#include <hal/hal_mem.h>
#include <hld/pan/pan_dev.h>
#include <hld/pan/pan.h>

#include <api/libloader/ld_boot_info.h>
#include "boot_system_data.h"

#ifdef HW_SET_BOOTLOGO
#undef HW_SET_BOOTLOGO
#endif
#ifdef HW_SET_GPIO
#undef HW_SET_GPIO
#endif
#ifdef HW_SET_BOOTTYPE
#undef HW_SET_BOOTTYPE
#endif
#ifdef HW_SET_PANTYPE
#undef HW_SET_PANTYPE
#endif

#define HW_SET_BOOTLOGO 0x9C
#define HW_SET_GPIO 0x104
#define HW_SET_BOOTTYPE 0x141
#define BOOT_TYPE_MASK 0x80
#define HW_SET_PANTYPE 0x9F
#define PAN_TYPE_MASK 0xE0

#define CHUNK_OTA_ADDR 0x40
#define CHUNK_OTA_LEN 0x44

#define BOOT_BASE_ADDR 0x80680000

#define US_TICKS        (sys_ic_get_cpu_clock()*1000000 / 2000000) //(SYS_CPU_CLOCK / 2000000)
#define WAIT_300MS      (300000 * US_TICKS)
#define WAIT_500MS		(500000 * US_TICKS)
#define WAIT_5MS        (5000   * US_TICKS)


/* Calcuate InternalBufferSize for 7-zip */
#define LZMA_BASE_SIZE  1846
#define LZMA_LIT_SIZE   768
#define BUFFER_SIZE     ((LZMA_BASE_SIZE + (LZMA_LIT_SIZE << (0 + 2))) * sizeof(UINT16))

#define WRITE_UINT8(uAddr, bVal)    do {*(volatile UINT8 *)(uAddr) = (bVal);} while(0)

#ifdef  PANEL_DISPLAY
struct pan_device *panel_dev;
#endif
struct sto_device *flash_dev;

UINT16 g_blogo_format = 0;  /* Bootloader logo parameters */
UINT16 g_sdhd_blogo_format = 0;
UINT8  sdhd_DAC_bProgressive = 0;
UINT8  g_crc_bad;
UINT32 otaloader_offset = 0;

extern int gzip_decompress(UINT8 *, UINT8 *, UINT8 *);
extern int un7zip(UINT8 *, UINT8 *, UINT8 *);

UINT8 check_program(void)
{
    UINT32 i;
    UINT8 *buffer;
    UINT32 data_id;
    UINT32 data_len;
    UINT32 data_off;
    UINT32 data_crc;
    UINT32 crc;
    UINT32 pointer = 0;

    do
    {
        data_id  = sto_fetch_long((UINT32)pointer + CHUNK_ID);
        data_len = sto_fetch_long((UINT32)pointer + CHUNK_LENGTH);
        data_off = sto_fetch_long((UINT32)pointer + CHUNK_OFFSET);
        data_crc = sto_fetch_long((UINT32)pointer + CHUNK_CRC);

        if (pointer != 0)
        {
            if (((data_id >> 16) & 0xFF) + ((data_id >> 24) & 0xFF) != 0xFF)
                return 1;
        }

        if (data_off > flash_dev->totol_size)
        {
            ERRMSG("@pointer[%08X] id[%08X] data_off[%08X] > flash size\n", \
                  pointer, data_id, data_off);
            return 2;
        }
        if ((data_off != 0) && (data_len > data_off))
        {
            ERRMSG("@pointer[%08X] id[%08X] data_len[%08X] > data_off[%08X]\n", \
                  pointer, data_id, data_len, data_off);
            return 3;
        }
        if (data_crc != NO_CRC)
        {
            if ((buffer = (UINT8 *)MALLOC(data_len)) == NULL)
                ASSERT(0);
            sto_get_data(flash_dev, buffer, pointer + 16, data_len);
            crc = MG_Table_Driven_CRC(0xFFFFFFFF, buffer, data_len);
            FREE(buffer);
            if (data_crc != crc)
            {
                ERRMSG("@pointer[%08X] id[%08X] crc[%08X] != data_crc[%08X]\n", \
                    pointer, data_id, crc, data_crc);
                return 4;
            }
        }
        pointer += data_off;
        if (pointer + CHUNK_HEADER_SIZE > flash_dev->totol_size)
        {
            ERRMSG("@pointer[%08X] no space for header\n", pointer);
            return 5;
        }

    } while (data_off);

    return 0;
}


UINT8 *expand(UINT32 offset, int unzip(UINT8 *, UINT8 *, UINT8 *))
{
	UINT8 *entry=0;
	UINT8 *buffer;
	UINT32 code_len,addr;
	UINT8 *codestart=0;

	if(offset!=0)
	{
		codestart = (UINT8 *)sto_fetch_long((UINT32)offset + CHUNK_CODESTART);
		entry = (UINT8 *)sto_fetch_long((UINT32)offset + CHUNK_ENTRY);

		/* Copy compressed code into DRAM */
		code_len = sto_fetch_long((UINT32)offset + CHUNK_LENGTH);
		sto_get_data(flash_dev, (void *)(BOOT_BASE_ADDR - code_len), offset + CHUNK_HEADER_SIZE, code_len);
	}
	else
	{
		code_len = sto_fetch_long((UINT32)offset + CHUNK_OTA_LEN);
		addr = sto_fetch_long((UINT32)offset + CHUNK_OTA_ADDR);
		sto_get_data(flash_dev, (void *)(BOOT_BASE_ADDR - code_len), addr, code_len);
	}

	if (codestart == 0 || codestart == (UINT8 *)0xFFFFFFFF)
		codestart = (UINT8 *)0x80000200;
	if (entry == 0 || entry == (UINT8 *)0xFFFFFFFF)
		entry = codestart;

    buffer = MALLOC(BUFFER_SIZE);
    if (buffer == NULL)
    {
        ERRMSG("Boot loader: No decompress buffer!\n");
        entry = 0;
    }
    else
    {
		if (unzip((void *)(BOOT_BASE_ADDR - code_len), codestart, buffer) != 0)
        {
            ERRMSG("Boot loader: Decompress error!\n");
            entry = 0;
        }
        FREE(buffer);
    }

    return entry;
}

void vdac_init(void)
{
	union BOOT_INFO *boot_info;
	unsigned char sdhdSelDacIndex[3]={2,1,0};
	UINT8 soc_vdac_num = 0;
	int i = 0;

	boot_info = get_boot_info();
	soc_vdac_num = 4;

	sdhd_tvenc_set_dac(boot_info->s_info.vdac_out[2], sdhdSelDacIndex);

	tvenc_set_dac(3, boot_info->s_info.vdac_out[3]);
}

void vpo_init(void)
{
	UINT32 boot_tick;

        //---reset video core: tve and vpo
        *(volatile UINT8 *)0xB8000060 = 0x09;
	osal_delay(5000);
	*(volatile UINT8 *)0xB8000060 = 0x00;

	declogo_attach_min(__MM_BUF_TOP_ADDR,__MM_FB0_Y_LEN+__MM_FB0_C_LEN);

	vdac_init();
	vpoInitGlobalValue();
	vpoInitRegs();
	sdhd_VPOInitRegs();
}

void boot_logo(void)
{
	UINT32 boot_tick;
	UINT32 chid;
	UINT32 offset;
	UINT32 len, i;
	UINT8 *buffer;
	UINT32 id = LOGO_ID;
	UINT16 blogo_para;
	UINT8  data[2];

	//chid = BOOT_ID_2;
	offset = 0;//sto_chunk_goto(&chid, 0xFFFFFFFF, 1);
	sto_get_data(flash_dev, data, offset + HW_SET_BOOTLOGO, 2);
	blogo_para = ((data[1] << 8) | data[0]);
	g_blogo_format = blogo_para & LOGO_FORMAT_MASK;
	g_blogo_format=0;
	if (blogo_para)
	{
		//---show logo here
		CHUNK_HEADER chuck_hdr;

		vpo_init();

		if (sto_get_chunk_header(id, &chuck_hdr) == 0)
			return;
		offset = (sto_chunk_goto(&id, 0xFFFFFFFF,1) + CHUNK_HEADER_SIZE);
		len = chuck_hdr.len - CHUNK_HEADER_SIZE + CHUNK_NAME;
		buffer = (void *)__MM_VBV_START_ADDR;
		sto_get_data(flash_dev, buffer, offset, len);
		decLogo((UINT8 *)buffer, len);
		//---show logo over
	}
}

#define bitmap_list             NULL
#define bitmap_list_num     0

struct pan_hw_info pan_hw_info_cp;
struct pan_configuration pan_config = {&pan_hw_info_cp, bitmap_list_num, bitmap_list};

/*support config OS resource
default setting: set_os_config(0,0,0,0,0);
*/
void set_os_config(UINT16 task_num, UINT16 sema_num, UINT16 flg_num, UINT16 mbf_num, UINT16 mutex_num)
{
	UINT16 * ptr = (UINT16*)0xa00001e0;

	ptr[0]= task_num;
	ptr[1]= sema_num;
	ptr[2]= flg_num;
	ptr[3]= mbf_num;
	ptr[4]= mutex_num;
	ptr[5]= task_num + sema_num + flg_num + mbf_num + mutex_num;
}

static void pin_mux_set(UINT32 value, UINT8 start_bit, UINT8 bit_num)
{
    UINT32 reg_tmp;
    UINT32 mask = 0, i, tmp;

    reg_tmp = *((volatile UINT32 *)0xb8000028);
    tmp = 1;
    for(i = 0; i < bit_num; i++)
    {
        tmp <<= 1;
        tmp |= 1;
    }
    mask = ~(tmp<<start_bit);

    reg_tmp &= mask;
    reg_tmp |= (value<<start_bit);

    *((volatile UINT32 *)0xb8000028) = reg_tmp;
}

INT32 enter_standby_check()
{
	union BOOT_INFO *boot_info;
	UINT32 boot_code;
	UINT8 boot_type;

	boot_info =  get_boot_info();

	// Cold bootup
	boot_code = *(UINT32 *)(0xA0000110);
	boot_type = *(UINT8 *)(SYS_FLASH_BASE_ADDR + HW_SET_BOOTTYPE);
	//if (((boot_type & BOOT_TYPE_MASK) || boot_info->s_info.standby_flag) && boot_code == 0xdead3bee)
	if ((boot_type & BOOT_TYPE_MASK)  && boot_code == 0xdead3bee)
	{
		/* If cold bootup and enter standby enabled, enter standby */
		return 1;
	}
	return 0;
}

void AppInit(void)
{
	union BOOT_INFO *boot_info;
	UINT8  ch, *t1, *t2, i;
	UINT32 boot_tick;
	UINT32 chid;
	UINT32 offset;
	UINT8 *main_entry;
	UINT8  boot_type, flash_speed;
	UINT32 boot_code;

#if (SYS_MAIN_BOARD == BOARD_S3602_DEMO || SYS_MAIN_BOARD == BOARD_ATSC_DEMO_00 || SYS_MAIN_BOARD == BOARD_DB_M3602_02V01)
	// Reset M3602 UART1 and UART2
	*(volatile UINT32 *)0xb8000080 = 0x00030000;
	osal_delay(10);
	*(volatile UINT32 *)0xb8000080 = 0x00000000;

	// Mute, avoid the pop noise when enable power on
#if (SYS_MUTE_MODE == GPIO_MUTE)
	HAL_GPIO_BIT_DIR_SET(MUTE_CIRCUIT_GPIO_NUM, HAL_GPIO_O_DIR);
	HAL_GPIO_BIT_SET(MUTE_CIRCUIT_GPIO_NUM, 1);
#else
	HAL_GPIO_BIT_DIR_SET(SCART_POWER_DOWN_GPIO_NUM, HAL_GPIO_O_DIR);
	HAL_GPIO_BIT_SET(SCART_POWER_DOWN_GPIO_NUM, 0);
#endif

	osal_task_sleep(50);

#if (SYS_MAIN_BOARD == BOARD_S3602_DEMO)
	// SYNC for RGHHV, GPIO#33
	*((unsigned long *)0xb8000088) = *((unsigned long *)0xb8000088) | 0x00003200;
	// Power on
	HAL_GPIO_BIT_DIR_SET(25, HAL_GPIO_O_DIR);
	HAL_GPIO_BIT_SET(25, 1);
#elif (SYS_MAIN_BOARD == BOARD_DB_M3602_02V01)
#if 0
	// CI/Flash pin mux disable
	*((unsigned long *)0xb8000088) =(*((unsigned long *)0xb8000088) & (~0x00000007));
	// Enable GPIO#6, GPIO#7 (Share with UART1), Enable UART0
	*((unsigned long *)0xb8000088) = *((unsigned long *)0xb8000088) & (~0x03ff0000);
	*((unsigned long *)0xb8000088) = *((unsigned long *)0xb8000088) | 0x00900000;

	// Power on
	HAL_GPIO_BIT_DIR_SET(61, HAL_GPIO_O_DIR);
	HAL_GPIO_BIT_SET(61, 0);

#else
	// Enable GPIO#5
	*((unsigned long *)0xb8000088) = *((unsigned long *)0xb8000088) | 0x00008000;

	// Enable GPIO#6, GPIO#7 (Share with UART1)
	*((unsigned long *)0xb8000088) = *((unsigned long *)0xb8000088) & (~0x03f00000);
	*((unsigned long *)0xb8000088) = *((unsigned long *)0xb8000088) | 0x00900000;

    // Enable UART0 TX/RX, bit19~18 = b00, bit17~16 = b00
	*((unsigned long *)0xb8000088) &= ~0x000f0000;

#ifdef GPIO_RGB_YUV_SWITCH
	g_rgb_yuv_switch = sys_get_rgb_yuv_switch();
#endif

	// Power on
	HAL_GPIO_BIT_DIR_SET(61, HAL_GPIO_O_DIR);
	HAL_GPIO_BIT_SET(61, 0);
	// Tuner LNB power on
	HAL_GPIO_BIT_DIR_SET(73, HAL_GPIO_O_DIR);
	HAL_GPIO_BIT_SET(73, 0);

#endif
#endif
#endif

    /* Initialize */
#ifndef ENABLE_EROM
	boot_uart_set_id(SCI_FOR_RS232);
	sci_16550uart_attach(1);	/* Use just 1 UART for RS232 */
    	sci_mode_set(boot_uart_get_id(), 115200, SCI_PARITY_EVEN);
	FIXED_PRINTF("APP  init ok\r\n");
#endif

#ifndef ENABLE_SERIAL_FLASH
	flash_info_pl_init();
	sto_local_flash_attach(NULL);
#else
	flash_info_sl_init();
	sto_local_sflash_attach(NULL);
#endif
	flash_dev = (struct sto_device *)dev_get_by_type(NULL, HLD_DEV_TYPE_STO);
	if (flash_dev == NULL)
	{
		PRINTF("Can't find FLASH device!\n");
	}
	sto_open(flash_dev);
	sto_chunk_init(0, flash_dev->totol_size);

#ifdef PANEL_DISPLAY
	t1 = (UINT8*)&pan_hw_info_cp;
	t2 = (UINT8*)(SYS_FLASH_BASE_ADDR+HW_SET_GPIO);//&pan_hw_info;

	for(i=0; i<sizeof(struct pan_hw_info); i++)
		*(t1+i) = *(t2+i);
#if (SYS_CHIP_MODULE == ALI_S3602)
	pan_hwscan_l_attach(&pan_config);
#else
	pan_common_attach(&pan_config);
#endif
	panel_dev = (struct pan_device *)dev_get_by_type(0, HLD_DEV_TYPE_PAN);
	pan_open(panel_dev);
	pan_display(panel_dev, " ON ", 4);
#endif

	sto_get_data(flash_dev, &flash_speed, HW_SET_FLASHSPEED, 1);
	flash_speed &= 0x3F;
	if (flash_speed == 0)
		flash_speed = HW_FLASH_DEFSPEED;
	HAL_ROM_MODE_SET(flash_speed, 0, 1, 0);

	/*boot info*/
	if(boot_sys_data_load(&boot_system_data)!=SUCCESS)
	{
		boot_sys_data_init();
	}
	boot_info = get_boot_info();

	MG_Setup_CRC_Table();
	g_crc_bad = check_program();

	/* Normal expend and boot */
	BL_PRINTF("bootloader: Start to boot!\n");

	if(boot_info->s_info.ota_flag || g_crc_bad!=0)
		offset = 0;
	else
	{
		if (g_crc_bad != 0)
		{
			BL_PRINTF("bootloader: CRC bad(%d).\n", g_crc_bad);
			goto BOOT_END;
		}
            	chid = CHUNKID_MAINCODE;
            	offset = sto_chunk_goto(&chid, CHUNKID_MAINCODE_MASK, 1);
	}

	if(offset != 0)
		boot_logo();

	main_entry = 0;
	main_entry = expand(offset, un7zip);

#ifdef PANEL_DISPLAY
        pan_display(panel_dev, "    ", 4);
#endif
        if (main_entry != 0)
        {
            boot_tick = read_tsc();
            do
            {
			if (sci_read_tm(boot_uart_get_id(), &ch, 5) == SUCCESS && ch == 'c')
                    break;
                else
                    ch = 0;
		}while (read_tsc() - boot_tick < WAIT_300MS);
            if (ch != 'c')
            {
			/* If cold bootup and enter standby enabled, enter standby */
			if(enter_standby_check()==1)
				sys_ic_enter_standby(0, 0);

#ifdef PANEL_DISPLAY
			pan_close(panel_dev);
#endif
			/*you can config OS resource here,
			default setting: set_os_config(0,0,0,0,0);	*/
			set_os_config(128,64,64,64,64);/*Call it before we enter main code*/
			exec(main_entry);
            }
        }

 BOOT_END:

#ifndef ENABLE_EROM
    lib_ash_shell();
#endif
	while(1);
}

